package com.example.estemplate.processor;

import com.example.estemplate.enums.TemplateOperateState;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.*;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.Executor;

/**
 * Elasticsearch模板管理处理器
 * 负责模板的创建、更新、比较等核心操作
 */
@Component
@Slf4j
public class TemplateManagerProcessor {
    /**
     * Elasticsearch高级客户端，用于与ES集群交互
     */
    private final RestHighLevelClient esClient;

    /**
     * 任务执行器，用于异步操作
     */
    private final Executor taskExecutor;

    /**
     * JSON处理工具
     */
    private final ObjectMapper objectMapper;

    /**
     * 构造函数，依赖注入
     *
     * @param esClient Elasticsearch客户端
     * @param taskExecutor 异步任务执行器
     * @param objectMapper JSON处理工具
     */
    public TemplateManagerProcessor(RestHighLevelClient esClient,
                                    @Qualifier("taskExecutor") Executor taskExecutor,
                                    ObjectMapper objectMapper) {
        // 初始化Elasticsearch客户端
        this.esClient = esClient;

        // 初始化任务执行器
        this.taskExecutor = taskExecutor;

        // 初始化JSON处理器
        this.objectMapper = objectMapper;
    }

    /**
     * 创建或更新单个模板。
     * 包含模板存在检查、内容比较等逻辑
     *
     * @param templateName 模板名称
     * @param templateContent 模板JSON内容
     * @return 操作状态枚举（操作成功/操作失败/无需操作）
     */
    public TemplateOperateState createOrUpdateTemplate(String templateName, String templateContent) {
        try {
            // 检查模板是否已存在
            boolean exists = checkTemplateExists(templateName);

            if (exists) {
                // 比较新旧模板内容差异，决定是否需要更新
                if (isTemplateChanged(templateName, templateContent)) {
                    // 执行模板更新操作
                    updateTemplate(templateName, templateContent);
                    log.info("createOrUpdateTemplate,Template {} updated successfully", templateName);
                    return TemplateOperateState.SUCCESS;
                } else {
                    // 模板内容无变化，无需更新
                    log.info("createOrUpdateTemplate,Template {} is up to date, no need to update", templateName);
                    return TemplateOperateState.NO_NEED;
                }
            } else {
                // 创建新模板
                createTemplate(templateName, templateContent);
                log.info("createOrUpdateTemplate,Template {} created successfully", templateName);
                return TemplateOperateState.SUCCESS;
            }
        } catch (Exception e) {
            log.error("createOrUpdateTemplate,Failed to process template: {}", templateName, e);
            return TemplateOperateState.FAIL;
        }
    }

    /**
     * 检查模板是否存在于Elasticsearch服务端
     *
     * @param templateName 要检查的模板名称
     * @return 是否存在
     * @throws IOException 网络或ES操作异常
     */
    public boolean checkTemplateExists(String templateName) throws IOException {
        // 构建模板存在检查请求
        IndexTemplatesExistRequest request = new IndexTemplatesExistRequest(templateName);

        // 执行检查请求
        boolean exists = esClient.indices().existsTemplate(request, RequestOptions.DEFAULT);

        log.info("checkTemplateExists,Template {} exists: {}", templateName, exists);

        return exists;
    }

    /**
     * 比较新旧模板内容是否发生变化
     *
     * @param templateName 模板名称
     * @param newTemplateContent 新模板内容
     * @return 是否发生变化
     * @throws IOException 网络或ES操作异常
     */
    private boolean isTemplateChanged(String templateName, String newTemplateContent) throws IOException {
        // 构建获取模板请求
        GetIndexTemplatesRequest request = new GetIndexTemplatesRequest(templateName);

        // 执行获取模板请求
        GetIndexTemplatesResponse response = esClient.indices().getIndexTemplate(request, RequestOptions.DEFAULT);

        // 检查模板是否存在
        if (response.getIndexTemplates().isEmpty()) {
            log.debug("isTemplateChanged,Template {} not found in Elasticsearch", templateName);
            return true;
        }

        try {
            // 获取现有模板元数据
            IndexTemplateMetadata existingTemplate = response.getIndexTemplates().get(0);

            // 创建JSON对象用于比较
            ObjectNode existingJson = objectMapper.createObjectNode();
            existingJson.put("name", existingTemplate.name());
            existingJson.put("order", existingTemplate.order());

            // 处理index_patterns
            ArrayNode patternsArray = existingJson.putArray("index_patterns");
            existingTemplate.patterns().forEach(patternsArray::add);

            // 处理settings
            if (existingTemplate.settings() != null) {
                existingJson.set("settings",
                        objectMapper.readTree(existingTemplate.settings().toString()));
            }

            // 处理mappings
            if (existingTemplate.mappings() != null) {
                existingJson.set("mappings",
                        objectMapper.readTree(existingTemplate.mappings().source().toString()));
            }

            // aliases
            if (existingTemplate.aliases() != null) {
                existingJson.set("aliases",
                        objectMapper.valueToTree(existingTemplate.aliases()));
            }

            // 版本
            existingJson.put("version", existingTemplate.version());
            //existingJson.set("_meta", objectMapper.valueToTree(existingTemplate.meta()));

            // 解析新模板内容
            JsonNode newNode = objectMapper.readTree(newTemplateContent);

            // 比较新旧模板内容
            boolean changed = !existingJson.equals(newNode);

            log.debug("isTemplateChanged,Template {} changed: {}", templateName, changed);

            return changed;
        } catch (Exception e) {
            log.error("isTemplateChanged,Template comparison failed for {}", templateName, e);
            return true; // 当比较失败时视为有变化
        }
    }

    /**
     * 删除并重新创建某个模板
     *
     * @param templateName 模板名称
     * @param templateContent 新模板内容
     * @throws IOException 网络或ES操作异常
     */
    public void deleteThenCreateTemplate(String templateName, String templateContent) throws IOException {
        // 先删除旧模板
        // 构建删除请求
        DeleteIndexTemplateRequest deleteRequest = new DeleteIndexTemplateRequest(templateName);

        // 执行删除操作
        esClient.indices().deleteTemplate(deleteRequest, RequestOptions.DEFAULT);

        // 创建新模板
        createTemplate(templateName, templateContent);

        log.info("delete Then Create Template: {}", templateName);
    }

    /**
     * 更新现有模板
     *
     * @param templateName    模板名称
     * @param templateContent 新模板内容
     * @throws IOException 网络或ES操作异常
     */
    public void updateTemplate(String templateName, String templateContent) throws IOException {
        // 直接更新模板（使用Elasticsearch 7.8+支持的直接更新API，不需要先删除再重新创建。更新模板时先删除再创建，存在短暂不可用窗口）
        PutIndexTemplateRequest request = new PutIndexTemplateRequest(templateName);
        request.source(templateContent, XContentType.JSON);
        esClient.indices().putTemplate(request, RequestOptions.DEFAULT);
        log.info("updated template: {}", templateName);
    }

    /**
     * 创建新模板
     *
     * @param templateName 模板名称
     * @param templateContent 模板内容
     * @throws IOException 网络或ES操作异常
     */
    public void createTemplate(String templateName, String templateContent) throws IOException {
        // 构建创建模板请求
        PutIndexTemplateRequest request = new PutIndexTemplateRequest(templateName);

        // 设置模板内容
        request.source(templateContent, XContentType.JSON);

        // 执行创建操作
        esClient.indices().putTemplate(request, RequestOptions.DEFAULT);

        log.info("Created template: {}", templateName);
    }
}